#include "EDBaseAfx.h"
#include "Base64.h"

#define SKIP (-1)
#define FAIL (-2)
#define PAD  (-3)
//---------------------------------------------------------------------------
static char *g64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static short g64DecodeArr[] = 
{
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,SKIP,SKIP,FAIL,FAIL,SKIP,FAIL,FAIL,	/* 0 */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* 1 */
	SKIP,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,0x3e,FAIL,FAIL,FAIL,0x3f,	/* 2 */
	0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,FAIL,FAIL,FAIL,PAD ,FAIL,FAIL,	/* 3 */
	FAIL,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,	/* 4 */
	0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,FAIL,FAIL,FAIL,FAIL,FAIL,	/* 5 */
	FAIL,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,	/* 6 */
	0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,FAIL,FAIL,FAIL,FAIL,FAIL,	/* 7 */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* 8 */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* 9 */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* A */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* B */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* C */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* D */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* E */
	FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,FAIL,	/* F */
	/* 0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F  */
};

/*
* Bit extracting macros
*/
#define Bot2(b) ((b)&0x3)
#define Bot4(b) ((b)&0xf)
#define Bot6(b) ((b)&0x3f)
#define Top2(b) Bot2((b)>>6)
#define Top4(b) Bot4((b)>>4)
#define Top6(b) Bot6((b)>>2)

#define MaxBytePerLine 68

/*
* the decoder
*/
#define EncodeThree64(bin,b64)	EncodeThreeFour(bin,b64,g64EncodeChars)

#define EncodeThreeFour(bin,b64,vector)                     \
do                                                          \
{                                                      \
	(bpl) += 4;                                             \
	*(b64)++ = vector[Top6((bin)[0])];                      \
	*(b64)++ = vector[Bot2((bin)[0])<<4 | Top4((bin)[1])];  \
	*(b64)++ = vector[Bot4((bin)[1])<<2 | Top2((bin)[2])];  \
	*(b64)++ = vector[Bot6((bin)[2])];                      \
}                                                           \
	while (0)


long Base64Coder::Encode(char *binPtr,long binLen,char *sixFourPtr)
{
	char *binSpot;									/* the byte currently being decoded */
	char *sixFourSpot = sixFourPtr;					/* the spot to which to copy the encoded chars */
	long bpl=0;
	char *end;										/* end of integral decoding */

	if (binLen)
	{/* we encode the integral multiples of three */
		end = binPtr + 3*(binLen/3);
		for (binSpot=binPtr; binSpot<end; binSpot+=3)
		{
			EncodeThree64(binSpot,sixFourSpot);/* *sixFourLen = sixFourSpot - sixFourPtr; */
		}
		/* now, copy the leftovers to the partial buffer */
		binLen = binLen % 3;
		if (binLen)
		{
			char bLeft[4]={0};
			for (int i=0; i<binLen; i++)  bLeft[i] = binSpot[i];
			EncodeThree64(bLeft,sixFourSpot);
			sixFourSpot[-1] = '=';
			if (binLen==1) sixFourSpot[-2] = '=';
		}
	}
	return (sixFourSpot - sixFourPtr);
}

long Base64Coder::Decode(const char *sixFourPtr, long sixFourLen, char *binPtr)
{
	short 	decode;													/* the decoded short */
	unsigned char c;												/* the decoded byte */
	/* we separate the short & the byte to the compiler can worry about byteorder */
	const char *end = sixFourPtr + sixFourLen;						/* stop decoding here */
	char *binSpot = binPtr;											/* current output character */
	short decoderState;												/* which of 4 bytes are we seeing now? */
	unsigned char partial;											/* partially decoded byte from/for last/next time */
	const char *sixFourStartPtr;

	/* fetch state from caller's buffer*/
	decoderState = 0;
	partial = 0;
	int nPad = 0;

	if (sixFourLen)
	{
		sixFourStartPtr = sixFourPtr;
		for (;sixFourPtr<end; sixFourPtr++)
		{
			switch (decode=g64DecodeArr[*sixFourPtr])
			{
			case SKIP:					/* skip whitespace */
			case FAIL:				    /* count invalid characters */
				break;
			case PAD:
				nPad ++;
				break;			        /* count pad characters */
			default:
				c = (char) decode;
				switch (decoderState)
				{
				case 0:
					partial = c<<2;
					decoderState++;
					break;
				case 1:
					*binSpot++ = partial|Top4(c);
					partial = Bot4(c)<<4;
					decoderState++;
					break;
				case 2:
					*binSpot++ = partial|Top6(c);
					partial = Bot2(c)<<6;
					decoderState++;
					break;
				case 3:
					*binSpot++ = partial|c;
					decoderState=0;
					break;
				} /* switch decoderState */
			} /* switch decode */
		} /* for sixFourPtr */
		switch (decoderState)
		{
		case 2:
		case 3:
			*binSpot++ = partial;
			partial=0;
			break;
		default:
			break;
		}
	} /* if sixFourLen */
	int nDec = (binSpot - binPtr);
	if (nPad > 0) nDec --;
	return nDec;
}

bool Base64Coder::SetWordWrap(long lBpl)
{
	if ((lBpl>0)&&((lBpl%4)==0)) 
	{
		__bWordWrap = true;
		__lBytesPerLine = lBpl;
		return true;
	}
	else 
	{
		return false;
	}
}
